package cn.framework.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cn.framework.dao.BaseHibernateDao;
import cn.framework.dao.model.OrderBy;
import cn.framework.dao.util.DaoUtil;
@Repository
@SuppressWarnings("unchecked")
public abstract class BaseHibernateDaoImpl<T> extends HibernateDaoSupport implements BaseHibernateDao {
	
	private final Logger logger=Logger.getLogger(getClass());
	
	private LinkedList<OrderBy> orderList=new LinkedList<OrderBy>();
	
	HibernateTemplate defaultHibernateTemplate;
	
	private boolean desc=false;
	@Autowired
	DaoUtil daoUtil;
	
	@Autowired
	public void BaseHibernateDaoImpl(@Qualifier("local-hibernateTemplate")HibernateTemplate hibernateTemplate){
		super.setHibernateTemplate(hibernateTemplate);
	}

	public void rebackDefaultDB(){
		super.setHibernateTemplate(defaultHibernateTemplate);
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public boolean delete(Object domain) {
		try {
			getHibernateTemplate().delete(domain);
			return true;
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public boolean deleteByKey(Serializable id) {
		try {
			getHibernateTemplate().delete(findById(id));
			return true;
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public boolean delete(List domainList) {
		try {
			getHibernateTemplate().deleteAll(domainList);
			return true;
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public T save(Object domain) {
		try {
			getHibernateTemplate().save(domain);
			return (T)domain;
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
			return null;
		}
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public T saveOrUpdate(Object domain) {
		try {
			getHibernateTemplate().saveOrUpdate(domain);
			return (T)domain;
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public T update(Object domain) {
		try {
			getHibernateTemplate().update(domain);
			return (T)domain;
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	public List<T>findByExample(Object value){
		return getHibernateTemplate().findByExample(value);
	}
	
	public List<T>findByExample(Object value,int firstResult,int maxResults){
		return getHibernateTemplate().findByExample(value, firstResult, maxResults);
	}
	public List<T> findAll() {
		DetachedCriteria date=DetachedCriteria.forClass(typeClass());
		date=addOrderBy(date);
		return getHibernateTemplate().findByCriteria(date);
	}

	public List<T> findAll(int firstResult, int size) {
		DetachedCriteria date=DetachedCriteria.forClass(typeClass());
		date=addOrderBy(date);
		return getHibernateTemplate().findByCriteria(date, firstResult, size);
	}
	
	public T findById(Serializable id) {
		return (T) getHibernateTemplate().get(typeClass(), id);
	}

	public List<T> findByProperty(String propertyName, Object value) {
		String[] propertyNames={propertyName};
		Object[] values={value};
		return findByProperty(propertyNames, values);
	}

	public List<T> findByProperty(String propertyName, Object value,Integer firstResult, Integer size) {
		String[] propertyNames={propertyName};
		Object[] values={value};
		return findByProperty(propertyNames, values,firstResult,size);
	}
	
	public T findByUnique(String propertyName, Object value){
		String[] propertyNames={propertyName};
		Object[] values={value};
		return findByUnique(propertyNames, values);
	}
	
	public T findByUnique(String[] propertyNames,Object[] values) {
		List<T> resultList=findByProperty(propertyNames, values,null,null);
		if(resultList!=null && resultList.size()>0){
			return resultList.get(0);
		}
		return null;
	}
	public List<T> findByProperty(String[] propertyNames, Object[] values) {
		return findByProperty(propertyNames, values,null,null);
	}
	
	public List<T> findByProperty(String[] propertyNames, Object[] values,
				Integer firstResult, Integer size){
			DetachedCriteria date=DetachedCriteria.forClass(typeClass());
			 for (int i = 0; i < propertyNames.length; i++) {
				String propertyName=propertyNames[i];
				Object value=values[i];
				date.add(Restrictions.eq(propertyName, value));
			}
			date=addOrderBy(date);
			if(firstResult!=null && size!=null){
				return getHibernateTemplate().findByCriteria(date, firstResult, size);
			}else{
				return getHibernateTemplate().findByCriteria(date);
			}
	 }
	 
	public List<T> findOrByProperty(String[] propertyNames, Object[] values,
			Integer firstResult, Integer size){
		DetachedCriteria date=DetachedCriteria.forClass(typeClass());
		 for (int i = 0; i < propertyNames.length; i++) {
			String propertyName=propertyNames[i];
			Object value=values[i];
			String neextPropertyName=propertyNames[i+1];
			Object nextValue=values[i+1];
			date.add(Restrictions.or(Restrictions.eq(propertyName, value),Restrictions.eq(neextPropertyName, nextValue)));
			if(i+1==propertyNames.length-1){
				break;
			}
		}
		date=addOrderBy(date);
		if(firstResult!=null && size!=null){
			return getHibernateTemplate().findByCriteria(date, firstResult, size);
		}else{
			return getHibernateTemplate().findByCriteria(date);
		}
	}
	
	public List<T> findOrLikeByProperty(String[] propertyNames, Object[] values){
		return findOrLikeByProperty(propertyNames, values, null, null);
	}
	public List<T> findOrLikeByProperty(String[] propertyNames, Object[] values,
			Integer firstResult, Integer size){
		DetachedCriteria date=DetachedCriteria.forClass(typeClass());
		 for (int i = 0; i < propertyNames.length; i++) {
			String propertyName=propertyNames[i];
			Object value=values[i];
			String neextPropertyName=propertyNames[i+1];
			Object nextValue=values[i+1];
			date.add(Restrictions.or(Restrictions.like(propertyName, value.toString(), MatchMode.ANYWHERE),Restrictions.eq(neextPropertyName, nextValue)));
			if(i+1==propertyNames.length-1){
				break;
			}
		}
		date=addOrderBy(date);
		if(firstResult!=null && size!=null){
			return getHibernateTemplate().findByCriteria(date, firstResult, size);
		}else{
			return getHibernateTemplate().findByCriteria(date);
		}
	}
	public List<T> findIn(String propertyName, Object[] value) {
		DetachedCriteria date=DetachedCriteria.forClass(typeClass());
		date.add(Restrictions.in(propertyName, value));
		return getHibernateTemplate().findByCriteria(date);
	}
	
	public int getCount(){
		String queryString="select count(*) from "+getTableName();
		List result = getHibernateTemplate().find(queryString);
		return ((Long)result.get(0)).intValue();
	}
	
	public int getCount(String propertyName, Object value){
		String[] propertyNames={propertyName};
		Object[] values={value};
		return getCount(propertyNames, values);
	}
	
	public int getCount(String[] propertyNames, Object[] values){
		String queryString="select count(*) from "+getTableName()+" as model where ";
		 for(String propertyName:propertyNames){   
	            queryString+=" model."+propertyName+"=? and";   
	        }   
	        queryString=StringUtils.removeEnd(queryString, "and"); 
		List result = getHibernateTemplate().find(queryString,values);
		return ((Long)result.get(0)).intValue();
	}

	public List<T> fuzzyFindByProperty(String propertyName, Object value) {
		String[] propertyNames={propertyName};
		Object[] values={value};
		return fuzzyFindByProperty(propertyNames, values);
	}
	
	public List<T> fuzzyFindByProperty(String propertyName, Object value,
			Integer firstResult, Integer size){
		String[] propertyNames={propertyName};
		Object[] values={value};
		return fuzzyFindByProperty(propertyNames, values,firstResult,size);
	}
	
	public List<T> fuzzyFindByProperty(String[] propertyNames, Object[] values) {
		return fuzzyFindByProperty(propertyNames, values, null, null);
	}

	public List<T> fuzzyFindByProperty(String[] propertyNames, Object[] values,
			Integer firstResult, Integer size) {
		DetachedCriteria date=DetachedCriteria.forClass(typeClass());
		for (int i = 0; i < propertyNames.length; i++) {
			String propertyName=propertyNames[i];
			Object value=values[i];
			date.add(Restrictions.like(propertyName,String.valueOf(value),MatchMode.ANYWHERE));
		}
		date=addOrderBy(date);
		if(firstResult!=null && size!=null){
			return getHibernateTemplate().findByCriteria(date, firstResult, size);
		}else{
			return getHibernateTemplate().findByCriteria(date);
		}
	}

	public String getDbTableName(){
		SessionFactory factory =getHibernateTemplate().getSessionFactory();
		Class cls = typeClass();
		AbstractEntityPersister classMetadata =(SingleTableEntityPersister)factory.getClassMetadata(cls);
		return classMetadata.getTableName();
	}

	public List<String> getDbCelName(){
		List<String> resultList=new ArrayList<String>();
		SessionFactory factory =getHibernateTemplate().getSessionFactory();
		Class cls = typeClass();
		AbstractEntityPersister classMetadata =(SingleTableEntityPersister)factory.getClassMetadata(cls);
		//添加主键
		resultList.addAll(Arrays.asList(classMetadata.getIdentifierColumnNames()));
		String[] propertyNames=classMetadata.getPropertyNames();
		for (String propertyName : propertyNames) {
			//判断是否一对多的对像,移除
			boolean isCollection=classMetadata.getClassMetadata().getPropertyType(propertyName).isCollectionType();
			if(!isCollection){
				String[] propertyColumnNames=classMetadata.getPropertyColumnNames(propertyName);
				for(String columnName:propertyColumnNames){
					resultList.add(columnName);
				}
			}
		}
		return resultList;
	}

	//取得泛型类型   
    @SuppressWarnings("unchecked")   
    protected Class<T> typeClass() {   
        return (Class<T>) ((ParameterizedType) getClass()   
                .getGenericSuperclass()).getActualTypeArguments()[0];   
    }   
    //取得泛型tableName   
    private String getTableName() {   
        return typeClass().getSimpleName();   
    }

    public DetachedCriteria addOrderBy(DetachedCriteria date){
    	if(orderList.isEmpty()){
    		initOrderList(date);
    	}
    	for (OrderBy orderBy : orderList) {
			if(orderBy.isDesc()){
				date.addOrder(Order.desc(orderBy.getPropertyName()));
			}else{
				date.addOrder(Order.asc(orderBy.getPropertyName()));
			}
		}
    	clearOrderList();
    	return date;
    }  
    
    public void clearOrderList(){
    	orderList=new LinkedList<OrderBy>();
    }
    
    public void initOrderList(DetachedCriteria date){
    	String orderPropertyName=daoUtil.getPrimaryKeyProperty(typeClass());
		if(desc){
			date.addOrder(Order.desc(orderPropertyName));
		}else{
			date.addOrder(Order.asc(orderPropertyName));
		}
    }

    public void addOrderBy(OrderBy orderBy){
    	this.orderList.addFirst(orderBy);
    }

	public boolean isDesc() {
		return desc;
	}

	public void setDesc(boolean desc) {
		this.desc = desc;
	}
}
